package dk.au.cs.bdsi.mail.representation;

import java.util.Arrays;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import dk.au.cs.bdsi.core.representation.CoreRepresentation;
import dk.au.cs.bdsi.core.representation.ServiceUri;
import dk.au.cs.bdsi.mail.domain.Header;
import dk.au.cs.bdsi.mail.domain.Mail;
import dk.au.cs.bdsi.mail.domain.MailStatus;

/**
 * Representation class for domain object {@link Mail}.
 */
@XmlRootElement(name = "mail", namespace = Representation.NAMESPACE)
public class MailRepresentation extends Representation {
	/**
	 * DAP {@link Link} which accepts a POST request with a supplied representation of 
	 * the state of the {@link Mail} as identified by the supplied URI. Note that
	 * you can retry sending a Mail (which has status {@link MailStatus#NotSent} by 
	 * updating it's status back to {@link MailStatus#Pending}.
	 */
	public final static String UpdateLink = Representation.RELATIONS_URI + "update";
	
	/**
	 * DAP {@link Link} which accepts a DELETE request to the given URI which identifies
	 * the specific resource to be deleted permanently.
	 */
	public final static String DeleteLink = Representation.RELATIONS_URI + "delete";
	
	@XmlElement(name = "to", namespace = Representation.NAMESPACE, required = true)
	private String to;
	
	@XmlElement(name = "from", namespace = Representation.NAMESPACE, required = true)
	private String from;
	
	@XmlElement(name = "subject", namespace = Representation.NAMESPACE, required = true)
	private String subject;
	
	@XmlElement(name = "body", namespace = Representation.NAMESPACE, required = true)
	private String body;
	
	@XmlElement(name = "status", namespace = Representation.NAMESPACE, required = false)
	private MailStatus status;
	
	@XmlElement(name = "header", namespace = Representation.NAMESPACE, required = false)
	private List<Header> headers;
	
	/**
	 * Intended for JAXB
	 */
	MailRepresentation() {
		// For JAXB
	}
	
	/**
	 * Construct a new representation based on the state of the given mail.
	 * 
	 * @param mail Mail to copy state from
	 * @param links List of DAP links
	 */
	public MailRepresentation(Mail mail, Link... links) {
		this.to = mail.getTo();
		this.from = mail.getFrom();
		this.subject = mail.getSubject();
		this.body = mail.getBody();
		this.status = mail.getStatus();
		this.headers = mail.getHeaders();
		setLinks(Arrays.asList(links));
	}
	
	/**
	 * Create a new {@link MailRepresentation} based on the domain object instance of
	 * a {@link Mail} as represented by the {@link ServiceUri}. I.e. it wraps a live
	 * domain instance into a representation and annotates it with appropriate {@link Link}'s
	 * based on the current state of the given mail. 
	 * 
	 * @param mail Live domain object
	 * @param mailUri URI of mail
	 * @return Representation of mail with appropriate links
	 */
	public static MailRepresentation createResponseRepresentation(Mail mail, ServiceUri mailUri) {
		switch (mail.getStatus()) {
		case Pending:
			return new MailRepresentation(mail, new Link(CoreRepresentation.SELF_REL_VALUE, mailUri));
		case NotSent:
			return new MailRepresentation(mail,
					new Link(UpdateLink, mailUri),
					new Link(DeleteLink, mailUri));
		case Sent:
			return new MailRepresentation(mail,
					new Link(DeleteLink, mailUri));
		default:
			throw new RuntimeException("Unknown mail status");
		}
	}
	
	/**
	 * Unmarshall domain object from XML document.
	 * 
	 * @param xml XML document
	 * @return  Domain object instance
	 */
	public static MailRepresentation fromXml(String xml) {
		return unmarshal(xml, MailRepresentation.class);
	}
	
	/**
	 * Marhsall this representation into an XML document.
	 * 
	 * @return XML document
	 */
	public String toXml() {
		return marshall(this);
	}
	
	@Override
	public String toString() {
		return toXml();
	}
	
	/**
	 * Retrieve a live domain object {@link Mail}, based on the state of this
	 * representation.
	 * 
	 * @return Mail with state from this representation
	 */
	public Mail getMail() {
		return new Mail(to, from, subject, body, status, headers);
	}
	
	/**
	 * Retrieve a Self-link (if any) from this representation
	 * 
	 * @return Self-link or null
	 */
	public Link getSelfLink() {
		return getLinkByName(SELF_REL_VALUE);
	}
	
	/**
	 * Retrieve a Update-link (if any) from this representation
	 * 
	 * @return Update-link or null
	 */
	public Link getUpdateLink() {
		return getLinkByName(UpdateLink);
	}
	
	/**
	 * Retrieve a delete link (if any) from this representation
	 * 
	 * @return Delete-link or null
	 */
	public Link getDeleteLink() {
		return getLinkByName(DeleteLink);
	}
}
